/*
 * $Log: LocalPlayer.java,v $ Revision 1.2 2006/11/05 13:58:00 wetzker more
 * merging - CLang + MF + some states still missing
 * 
 * Revision 1.1.28.1 2006/07/26 15:28:27 endert Formatted Code
 * 
 * Revision 1.1 2006/03/13 07:37:45 axle moved from ROOT/src for Maven 2
 * Buildsystem and Team/Soccerscope separation
 * 
 * Revision 1.5 2006/03/07 12:46:01 endert Merge after refactoring: - New
 * State-Interface: One Method for each action type - Action Bean removed -
 * ActionFactorys for helper methods, States calculate main-Action themselves -
 * formatted code - added State-List in Monitor (Context-Menu of Player) - other
 * smaller stuff
 * 
 * Revision 1.4.2.2 2006/03/06 23:17:52 endert Removed warnings, added
 * State-Overview to Context-Menu!
 * 
 * Revision 1.4.2.1 2006/02/25 22:07:24 wetzker formated
 * 
 * Revision 1.4 2006/02/22 16:04:42 endert This is done: - Rebound SoccerScope
 * to Agents (can be added or removed in config-file) - Situations now all
 * extend abstract class Situation - Refactorings: ActionBean _ removed from
 * Attributes - SoccerScope will automatically connect to host given in
 * Start-Call - Trainer uses the same host as soccerscope - Removed unused stuff
 * (old confs, bat, etc.) - Code formatted (good luck when merging) - organized
 * imports - all that i forgot to mention here
 * 
 * Revision 1.3 2005/10/25 20:51:37 endert Added Training-Editor to SoccerScope,
 * restructuring in some old classes and some experimental stuff (goals,
 * commitments, mental model)
 * 
 * Revision 1.2.10.1 2005/10/10 11:05:12 endert New Training-Scenario Editor!
 * There exists a dialog, which opens on middle-mouse click on the field. From
 * here, the Trainings-Scenario may be specified in a more comfortable manner.
 * The Trainings-Scenario is also visualized on the Field - using the
 * TrainingModelLayer. The TrainingModel itself contains all data specified in
 * the scenario, and therefor can be serialized to HDD, using an XML-Stream
 * generator (XStream). The Scenario editor is still under development.
 * 
 * Revision 1.2 2005/06/19 19:44:36 till added parameter teamSide to constructor
 * of soccerscope.model.Player
 * 
 * Revision 1.1 2005/04/20 10:39:03 endert Fresh Team Repository installation
 * from /home/e/endert/Robocup to here.
 * 
 * Revision 1.2 2005/02/06 12:08:14 haeder commented out illegal imports
 * 
 * Revision 1.1 2005/02/06 07:17:26 maketi Added soccerscope with connection to
 * our world model
 * 
 * Revision 1.10 2003/05/30 04:56:00 newone *** empty log message ***
 * 
 * Revision 1.9 2003/01/04 08:34:38 koji
 * {kick,dash,turn}������Ȥ��ɤ߹���褦�˽�5
 * 
 * Revision 1.8 2002/10/17 09:32:46 koji canTackle�ν�5
 * 
 * Revision 1.7 2002/10/10 09:43:32 koji ͥ*�ΰ�η׻��β���
 * (Direction-Distance Table)
 * 
 * Revision 1.6 2002/10/04 10:39:06 koji
 * Tuple,Point,Vector�Υѥå������ѹ���Geometry,Circle,Line,Rectangle�ɲ�
 * 
 * Revision 1.5 2002/10/01 14:11:02 koji support Preference, gif format
 * 
 * Revision 1.4 2002/09/12 17:07:03 koji
 * HeteroParam�d�ä��ѹ���Hetro->Hetero
 * 
 * Revision 1.3 2002/09/05 10:20:19 taku-ako �إƥ�ץ쥤�䡼���б�
 * 
 * Revision 1.2 2002/06/04 05:58:09 koji Tackle�θ�̤�ɽ��
 * 
 * Revision 1.1.1.1 2002/03/01 14:12:54 koji CVS�����
 * 
 */

package soccerscope.model;

// import robocup.component.worldmodel.WorldModelChangeEvent;
// import robocup.component.worldmodel.WorldModelChangeListener;
import java.util.Vector;

import soccerscope.util.geom.Geometry;
import soccerscope.util.geom.Line2f;
import soccerscope.util.geom.Point2f;
import soccerscope.util.geom.Vector2f;

public class LocalPlayer
    extends Player {

  /**
   * 
   */
  private static final long serialVersionUID = -99301823996307632L;

  public Vector             unknownPlayers;

  public Vector             enemyPlayers;

  public Vector             friendlyPlayers;

  public int                number;

  public LocalPlayer(
      int teamSide) {

    super(teamSide);
    this.unknownPlayers = new Vector();
    this.enemyPlayers = new Vector();
    this.friendlyPlayers = new Vector();
  }

  public void setPlayer(LocalPlayer p) {

    super.setPlayer(p);
    this.friendlyPlayers = p.friendlyPlayers;
    this.enemyPlayers = p.enemyPlayers;
    this.unknownPlayers = p.unknownPlayers;
  }

  @Override
  public boolean isOffside(float line) {

    return this.side == Team.LEFT_SIDE && this.pos.x > line || this.side == Team.RIGHT_SIDE && this.pos.x < line;
  }

  @Override
  public boolean isKickable(Point2f ball) {

    return this.pos.distance(ball) <= (Param.BALL_SIZE + this.getSize() + HeteroParam
        .get(this.type).kickable_margin);
  }

  @Override
  public boolean isCatchable(Point2f ball) {

    return this.pos.distance(ball) <= Param.GOALIE_CATCHABLE_AREA_LENGTH;
  }

  @Override
  public boolean canTackle(Point2f ball) {

    Vector2f rb = this.toRelative(ball);
    return (rb.x > 0 && rb.x <= Param.TACKLE_DIST && Math.abs(rb.y) <= Param.TACKLE_WIDTH / 2);
  }

  @Override
  public void step() {

    this.pos.add(this.vel);
    this.pos.add(this.acc);
    this.vel.add(this.acc);
    this.vel.scale(HeteroParam.get(this.type).player_decay);
    this.acc.set(0, 0);
  }

  @Override
  public float getKickable() {

    return (Param.BALL_SIZE + this.getSize() + HeteroParam.get(this.type).kickable_margin);
  }

  @Override
  public float getMoveMax() {

    return (1 + Param.PLAYER_RAND) * HeteroParam.get(this.type).player_speed_max;
  }

  @Override
  public float getTurnAngle() {

    return 180 / (1 + HeteroParam.get(this.type).inertia_moment * this.vel.r());
  }

  @Override
  public float getDashAccelerationMax() {

    return HeteroParam.get(this.type).dash_power_rate * HeteroParam
        .get(this.type).effort_max * Param.POWER_MAX;
  }

  @Override
  public Vector2f getVector() {

    return Vector2f.polar2vector(this.getSize(), this.angle);
  }

  @Override
  public Line2f getLine() {

    Point2f t = new Point2f(this.pos);
    t.add(this.getVector());
    return new Line2f(this.pos, t);
  }

  @Override
  public Vector2f toRelative(Point2f p) {

    return new Vector2f(this.pos, p).rotate(-this.angle);
  }

  @Override
  public float getAngleFromBody(Point2f p) {

    return Geometry.normalizeAngle(this.pos.dir(p) - this.angle);
  }

  @Override
  public float getAngleFromNeck(Point2f p) {

    return Geometry
        .normalizeAngle(this.pos.dir(p) - this.angle - this.angleNeck);
  }

  @Override
  public Vector2f getDashAccelerate(float power) {

    // effort == 1 �Ȥ��Ʒ׻�
    return Vector2f.polar2vector(
        HeteroParam.get(this.type).dash_power_rate * power, this.angle);
  }

  @Override
  public Vector2f getKickAccelerate(Point2f ball,
      float power,
      float dir) {

    Vector2f diff = new Vector2f();
    diff.sub(ball, this.pos);
    float effectiveKickPowerRate = Param.KICK_POWER_RATE;
    effectiveKickPowerRate *= 1.0 - (diff.length() - Param.BALL_SIZE - this
        .getSize()) / HeteroParam.get(this.type).kickable_margin * 0.25 - Math
        .abs(Geometry.normalizeAngle(diff.th() - this.angle)) / 180.0 * 0.25;
    return Vector2f.polar2vector(effectiveKickPowerRate * power, Geometry
        .normalizeAngle(this.angle + dir));
  }

  @Override
  public Vector2f getFinalMove() {

    return Vector2f.polar2vector(this.calcFinalDistance(), this.vel.th());
  }

  @Override
  public float getTackleProb(Point2f p) {

    Vector2f tmp = this.toRelative(p);
    float prob = (float) (1.0 - (Math.pow(tmp.x / Param.TACKLE_DIST,
        Param.TACKLE_EXPONENT) + Math.pow(
        Math.abs(tmp.y) / (Param.TACKLE_WIDTH / 2.0f), Param.TACKLE_EXPONENT)));
    return prob;
  }

  // ����t���®�� (a == 0)(t >= 0)
  @Override
  public float calcVelocityAfterNTime(int t) {

    float v = this.vel.r();
    float d = HeteroParam.get(this.type).player_decay;
    return v * (float) Math.pow(d, t);
  }

  // ����t��ΰ�ư��Υ (a == 0)(t >= 0)
  @Override
  public float calcDistanceAfterNTime(int t) {

    float v = this.vel.r();
    float d = HeteroParam.get(this.type).player_decay;
    return v * (1 - (float) Math.pow(d, t)) / (1 - d);
  }

  // ����t���®�� (a != 0)(t >= 0)
  @Override
  public float calcVelocityAfterNTime(float a,
      int t) {

    float d = HeteroParam.get(this.type).player_decay;
    return this.calcVelocityAfterNTime(t) + a * ((1 - (float) Math
        .pow(d, t + 1)) / (1 - d) - 1);
  }

  // ����t��ΰ�ư��Υ (a != 0)(t >= 0)
  @Override
  public float calcDistanceAfterNTime(float a,
      int t) {

    float d = HeteroParam.get(this.type).player_decay;
    return this.calcDistanceAfterNTime(t) + (a / (1 - d)) * (t + 1 - (1 - (float) Math
        .pow(d, t + 1)) / (1 - d));
  }

  // �ǽ�®�� (t -> ��)(a != 0)
  @Override
  public float calcFinalVelocity(float a) {

    float d = HeteroParam.get(this.type).player_decay;
    return a * d / (1 - d);
  }

  // �ǽ���ư��Υ (t -> ��)(a == 0)
  @Override
  public float calcFinalDistance() {

    float v = this.vel.r();
    float d = HeteroParam.get(this.type).player_decay;
    return v / (1 - d);
  }

  // ®��vt��ã����Τˤ�������� (a == 0)(vt <
  // v)
  @Override
  public int calcTimeByVelocity(float vt) {

    // float v = this.vel.r();
    float d = HeteroParam.get(this.type).player_decay;
    return (int) Math
        .ceil(((float) Math.log(vt) / (float) Math.log(d)) - ((float) Math
            .log(this.vel.r()) / (float) Math.log(d)));
  }

  // ��Υdist��ư����Τˤ�������� (a ==
  // 0)(dist >= 0)
  @Override
  public int calcTimeByDistance(float dist) {

    float v = this.vel.r();
    float d = HeteroParam.get(this.type).player_decay;
    return (int) Math
        .ceil(((float) Math.log(1 - dist * (1 - d) / v)) / ((float) Math.log(d)));
  }

  // ®��vt��ã����Τˤ�������� (a !=
  // 0)(max(a, v) < vt <= speed_max)
  @Override
  public int calcTimeByVelocity(float vt,
      float a) {

    float v = this.vel.r();
    float d = HeteroParam.get(this.type).player_decay;
    return (int) Math.ceil(((float) Math
        .log((vt - a / (1 - d) + a) / (v / d - a / (1 - d))) / (float) Math
        .log(d)) - 1);
  }

  // ��Υdist��ư����Τˤ�������� (a !=
  // 0)(dist >= 0)
  @Override
  public int calcTimeByDistance(float dist,
      float a) {

    int time = (int) dist;
    float tmp_dist = this.calcDistanceAfterNTime(a, time);
    if (tmp_dist == dist) {
      return time;
    }
    else if (tmp_dist < dist) {
      while (tmp_dist < dist) {
        time++;
        tmp_dist = this.calcDistanceAfterNTime(a, time);
      }
    }
    else {
      while (tmp_dist > dist) {
        time--;
        tmp_dist = this.calcDistanceAfterNTime(a, time);
      }
      time++;
    }
    return time;
  }

  // ��Υdist��ư����Τ˺���¤�����������
  // (a != 0)(dist >= 0)
  // =�ȥåץ��ԡ��ɤξ��֤�t�ϥ%å��夷�Ƥ��������
  @Override
  public int estimateMinTimeByDistance(float dist,
      float a) {

    float d = HeteroParam.get(this.type).player_decay;
    return (int) Math.ceil(dist / (a + (a * d) / (1 - d)));
  }

  public static void main(String arg[]) {

    LocalPlayer p = new LocalPlayer(Team.NEUTRAL);
    float acc = p.getDashAccelerationMax();

    long time, diff;

    time = System.currentTimeMillis();
    for (int i = 0; i < 22; i++) {
      for (float d = 0.0f; d < 100; d += 0.5f) {
        p.calcTimeByDistance(d, acc);
      }
    }
    diff = System.currentTimeMillis();
    System.out.println("normal: " + (diff - time));

    time = diff;
    float table[] = new float[120];
    for (int i = 0; i < 120; i++) {
      table[i] = p.calcDistanceAfterNTime(acc, i);
    }
    diff = System.currentTimeMillis();
    System.out.println("make: " + (diff - time));

    time = diff;
    for (int i = 0; i < 22; i++) {
      for (float d = 0.0f; d < 100; d += 0.5f) {
        p.search(table, d);
      }
    }
    diff = System.currentTimeMillis();
    System.out.println("cache: " + (diff - time));
  }

  @Override
  public int search(float[] t,
      float key) {

    int idx = (int) key;
    float tmp = t[idx];
    if (tmp == key) {
      return idx;
    }
    else if (tmp < key) {
      while (tmp < key) {
        idx++;
        tmp = t[idx];
      }
    }
    else {
      while (tmp > key) {
        idx--;
        tmp = t[idx];
      }
      idx++;
    }
    return idx;
  }

}
